{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "XGCdmDAKpLuf"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors.\n",
        "\n",
        "Licensed under the Apache License, Version 2.0 (the \"License\");"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "GF4d1XplpLGF"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W1L3zJP6pPGD"
      },
      "source": [
        "# FGSM을 이용한 적대적 샘플 생성\n",
        "\n",
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/generative/adversarial_fgsm\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />TensorFlow.org에서 보기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />구글 코랩(Colab)에서 실행하기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />깃허브(GitHub) 소스 보기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/tutorials/generative/adversarial_fgsm.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Download notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yJ-t14sot8iX"
      },
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
        "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
        "이 번역에 개선할 부분이 있다면\n",
        "[tensorflow/docs-l10n](https://github.com/tensorflow/docs-l10n/) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
        "문서 번역이나 리뷰에 참여하려면\n",
        "[docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로\n",
        "메일을 보내주시기 바랍니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "8dn1-g8BpPDx"
      },
      "source": [
        "이 튜토리얼에서는 Ian Goodfellow *et al*의 [Explaining and Harnessing Adversarial Examples](https://arxiv.org/abs/1412.6572)에 기술된 FGSM(Fast Gradient Signed Method)을 이용해 적대적 샘플(adversarial example)을 생성하는 방법에 대해 소개합니다. FGSM은 신경망 공격 기술들 중 초기에 발견된 방법이자 가장 유명한 방식 중 하나입니다.\n",
        "\n",
        "## 적대적 샘플이란?\n",
        "\n",
        "적대적 샘플이란 신경망을 혼란시킬 목적으로 만들어진 특수한 입력으로, 신경망으로 하여금 샘플을 잘못 분류하도록 합니다. 비록 인간에게 적대적 샘플은 일반 샘플과 큰 차이가 없어보이지만, 신경망은 적대적 샘플을 올바르게 식별하지 못합니다. 이와 같은 신경망 공격에는 여러 종류가 있는데, 본 튜토리얼에서는 화이트 박스(white box) 공격 기술에 속하는 FGSM을 소개합니다. 화이트 박스 공격이란 공격자가 대상 모델의 모든 파라미터값에 접근할 수 있다는 가정 하에 이루어지는 공격을 일컫습니다. 아래 이미지는 Goodfellow et al에 소개된 가장 유명한 적대적 샘플인 판다의 사진입니다.\n",
        "\n",
        "![Adversarial Example](images/adversarial_example.png)\n",
        "\n",
        "원본 이미지에 특정한 작은 왜곡을 추가하면 신경망으로 하여금 판다를 높은 신뢰도로 긴팔 원숭이로 잘못 인식하도록 만들 수 있습니다. 이하 섹션에서는 이 왜곡 추가 과정에 대해 살펴보도록 하겠습니다.\n",
        "\n",
        "## FGSM\n",
        "FGSM은 신경망의 그래디언트(gradient)를 이용해 적대적 샘플을 생성하는 기법입니다. 만약 모델의 입력이 이미지라면, 입력 이미지에 대한 손실 함수의 그래디언트를 계산하여 그 손실을 최대화하는 이미지를 생성합니다. 이처럼 새롭게 생성된 이미지를 적대적 이미지(adversarial image)라고 합니다. 이 과정은 다음과 같은 수식으로 정리할 수 있습니다:\n",
        "\n",
        "$$adv\\_x = x + \\epsilon*\\text{sign}(\\nabla_xJ(\\theta, x, y))$$\n",
        "\n",
        "각 기호에 대한 설명은 다음과 같습니다.\n",
        "\n",
        "*   adv_x : 적대적 이미지.\n",
        "*   x : 원본 입력 이미지.\n",
        "*   y : 원본 입력 레이블(label).\n",
        "*   $\\epsilon$ : 왜곡의 양을 적게 만들기 위해 곱하는 수.\n",
        "*   $\\theta$ : 모델의 파라미터.\n",
        "*   $J$ : 손실 함수.\n",
        "\n",
        "여기서 흥미로운 사실은 입력 이미지에 대한 그래디언트가 사용된다는 점입니다. 이는 손실을 최대화하는 이미지를 생성하는 것이 FGSM의 목적이기 때문입니다. 요약하자면, 적대적 샘플은 각 픽셀의 손실에 대한 기여도를 그래디언트를 통해 계산한 후, 그 기여도에 따라 픽셀값에 왜곡을 추가함으로써 생성할 수 있습니다. 각 픽셀의 기여도는 연쇄 법칙(chain rule)을 이용해 그래디언트를 계산하는 것으로 빠르게 파악할 수 있습니다. 이것이 입력 이미지에 대한 그래디언트가 쓰이는 이유입니다. 또한, 대상 모델은 더 이상 학습하고 있지 않기 때문에 (따라서 신경망의 가중치에 대한 그래디언트는 필요하지 않습니다) 모델의 가중치값은 변하지 않습니다. FGSM의 궁극적인 목표는 이미 학습을 마친 상태의 모델을 혼란시키는 것입니다.\n",
        "\n",
        "이제 사전 훈련된 모델을 공격해보도록 하겠습니다. 이 튜토리얼에서 사용될 모델은 [ImageNet](http://www.image-net.org/)에서 사전 훈련된 [MobileNetV2](https://www.tensorflow.org/versions/r2.0/api_docs/python/tf/keras/applications/MobileNetV2) 모델입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "vag2WYR6yTOC"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "import matplotlib as mpl\n",
        "import matplotlib.pyplot as plt\n",
        "\n",
        "mpl.rcParams['figure.figsize'] = (8, 8)\n",
        "mpl.rcParams['axes.grid'] = False"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wiTHY8dqxzx7"
      },
      "source": [
        "사전 훈련된 MobileNetV2 모델과 ImageNet의 클래스(class) 이름들을 불러옵니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "nqhk2vYx6Ag0"
      },
      "outputs": [],
      "source": [
        "pretrained_model = tf.keras.applications.MobileNetV2(include_top=True,\n",
        "                                                     weights='imagenet')\n",
        "pretrained_model.trainable = False\n",
        "\n",
        "# ImageNet 클래스 레이블\n",
        "decode_predictions = tf.keras.applications.mobilenet_v2.decode_predictions"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "f2cLrJH0zpfC"
      },
      "outputs": [],
      "source": [
        "# 이미지가 MobileNetV2에 전달될 수 있도록 전처리해주는 헬퍼 메서드(helper function)\n",
        "def preprocess(image):\n",
        "  image = tf.cast(image, tf.float32)\n",
        "  image = image/255\n",
        "  image = tf.image.resize(image, (224, 224))\n",
        "  image = image[None, ...]\n",
        "  return image\n",
        "\n",
        "# 확률 벡터에서 레이블을 추출해주는 헬퍼 메서드\n",
        "def get_imagenet_label(probs):\n",
        "  return decode_predictions(probs, top=1)[0][0]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "iEZaMVFgSUA-"
      },
      "source": [
        "## 원본 이미지\n",
        "Mirko [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)의 [래브라도 리트리버](https://commons.wikimedia.org/wiki/File:YellowLabradorLooking_new.jpg) 샘플 이미지를 이용해 적대적 샘플을 생성합니다. 첫 단계로, 원본 이미지를 전처리하여 MobileNetV2 모델에 입력으로 제공합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "wpYrQ4OQSYWk"
      },
      "outputs": [],
      "source": [
        "image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')\n",
        "image_raw = tf.io.read_file(image_path)\n",
        "image = tf.image.decode_image(image_raw)\n",
        "\n",
        "image = preprocess(image)\n",
        "image_probs = pretrained_model.predict(image)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "mvPlta_uSbuI"
      },
      "source": [
        "이미지를 살펴봅시다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "99Jc-SNoSZot"
      },
      "outputs": [],
      "source": [
        "plt.figure()\n",
        "plt.imshow(image[0])\n",
        "_, image_class, class_confidence = get_imagenet_label(image_probs)\n",
        "plt.title('{} : {:.2f}% Confidence'.format(image_class, class_confidence*100))\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kElVTbF690CF"
      },
      "source": [
        "## 적대적 이미지 생성하기\n",
        "\n",
        "### FGSM 실행하기\n",
        "첫번째 단계는 샘플 생성을 위해 원본 이미지에 가하게 될 왜곡을 생성하는 것입니다. 앞서 살펴보았듯이, 왜곡을 생성할 때에는 입력 이미지에 대한 그래디언트를 사용합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FhZxlOnuBCVr"
      },
      "outputs": [],
      "source": [
        "loss_object = tf.keras.losses.CategoricalCrossentropy()\n",
        "\n",
        "def create_adversarial_pattern(input_image, input_label):\n",
        "  with tf.GradientTape() as tape:\n",
        "    tape.watch(input_image)\n",
        "    prediction = pretrained_model(input_image)\n",
        "    loss = loss_object(input_label, prediction)\n",
        "\n",
        "  # 입력 이미지에 대한 손실 함수의 기울기를 구합니다.\n",
        "  gradient = tape.gradient(loss, input_image)\n",
        "  # 왜곡을 생성하기 위해 그래디언트의 부호를 구합니다.\n",
        "  signed_grad = tf.sign(gradient)\n",
        "  return signed_grad"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RbuftX0eSlDQ"
      },
      "source": [
        "생성한 왜곡을 시각화해 볼 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "rVjnb6M7Smv4"
      },
      "outputs": [],
      "source": [
        "# 이미지의 레이블을 원-핫 인코딩 처리합니다.\n",
        "labrador_retriever_index = 208\n",
        "label = tf.one_hot(labrador_retriever_index, image_probs.shape[-1])\n",
        "label = tf.reshape(label, (1, image_probs.shape[-1]))\n",
        "\n",
        "perturbations = create_adversarial_pattern(image, label)\n",
        "plt.imshow(perturbations[0])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "DKKSFHjwCyQH"
      },
      "source": [
        "왜곡 승수 엡실론(epsilon)을 바꿔가며 다양한 값들을 시도해봅시다. 위의 간단한 실험을 통해 엡실론의 값이 커질수록 네트워크를 혼란시키는 것이 쉬워짐을 알 수 있습니다. 하지만 이는 이미지의 왜곡이 점점 더 뚜렷해진다는 단점을 동반합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "dBtG0Kl5SspV"
      },
      "outputs": [],
      "source": [
        "def display_images(image, description):\n",
        "  _, label, confidence = get_imagenet_label(pretrained_model.predict(image))\n",
        "  plt.figure()\n",
        "  plt.imshow(image[0])\n",
        "  plt.title('{} \\n {} : {:.2f}% Confidence'.format(description,\n",
        "                                                   label, confidence*100))\n",
        "  plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "3DA8g-Zp69J4"
      },
      "outputs": [],
      "source": [
        "epsilons = [0, 0.01, 0.1, 0.15]\n",
        "descriptions = [('Epsilon = {:0.3f}'.format(eps) if eps else 'Input')\n",
        "                for eps in epsilons]\n",
        "\n",
        "for i, eps in enumerate(epsilons):\n",
        "  adv_x = image + eps*perturbations\n",
        "  adv_x = tf.clip_by_value(adv_x, 0, 1)\n",
        "  display_images(adv_x, descriptions[i])"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "fxt5VfnXHQT6"
      },
      "source": [
        "## 다음 단계\n",
        "\n",
        "이 튜토리얼에서 적대적 공격에 대해서 알아보았으니, 이제는 이 기법을 다양한 데이터넷과 신경망 구조에 시험해볼 차례입니다. 새로 만든 모델에 FGSM을 시도해보는 것도 가능할 것입니다. 엡실론 값을 바꿔가며 신경망의 샘플 신뢰도가 어떻게 변하는지 살펴볼 수도 있습니다.\n",
        "\n",
        "FGSM은 그 자체로도 강력한 기법이지만 이후 다른 연구들에서 발견된 보다 더 효과적인 적대적 공격 기술들의 시작점에 불과합니다. 또한, FGSM의 발견은 적대적 공격 뿐만 아니라 더 견고한 기계 학습 모델을 만들기 위한 방어 기술에 대한 연구도 촉진시켰습니다. 적대적 공격과 방어 기술에 대한 전반적인 조망은 이 [문헌](https://arxiv.org/abs/1810.00069)에서 볼 수 있습니다.\n",
        "\n",
        "다양한 적대적 공격과 방어 기술의 구현 방법이 궁금하다면, 적대적 샘플 라이브러리 [CleverHans](https://github.com/tensorflow/cleverhans)를 참고합니다."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "adversarial_fgsm.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
